/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

namespace atomic
{
#if defined(NX_OS_WIN32) || defined(NX_OS_WINCE)
    typedef LONG            norm_t;
#elif defined(NX_OS_WIN64)
    typedef LONG64          norm_t;
#elif defined(NX_OS_LINUX)
    typedef long            norm_t;
#else
    typedef long            norm_t;
#endif

#ifndef NX_SINGLE_THREAD
    typedef volatile norm_t type_t;
#else /*NX_SINGLE_THREAD*/
    typedef norm_t          type_t;
#endif/*NX_SINGLE_THREAD*/
}

/*
    atomic operators
*/

#ifndef NX_SINGLE_THREAD

#if defined(NX_CC_GNUC)

#   define nx_atomic_inc(var)           __sync_fetch_and_add        (&(var), 1)
#   define nx_atomic_dec(var)           __sync_fetch_and_sub        (&(var), 1)
#   define nx_atomic_add(var, val)      __sync_fetch_and_add        (&(var), (val))
#   define nx_atomic_sub(var, val)      __sync_fetch_and_sub        (&(var), (val))
#   define nx_atomic_set(var, val)      __sync_lock_test_and_set    (&(var), (val))
#   define nx_atomic_cas(var, cmp, val) __sync_bool_compare_and_swap(&(var), (cmp), (val))

#if NX_CHECK_GNUC(4, 2, 0)
#   define nx_atomic_barrier()          __sync_synchronize()
#else
#   define nx_atomic_barrier()          __asm__ __volatile__("":::"memory")
#endif

#elif defined(NX_OS_WIN32) || defined(NX_OS_WINCE)

#   define nx_atomic_inc(var)           InterlockedExchangeAdd      (&(var), 1)
#   define nx_atomic_dec(var)           InterlockedExchangeAdd      (&(var),-1)
#   define nx_atomic_add(var, val)      InterlockedExchangeAdd      (&(var), (val))
#   define nx_atomic_sub(var, val)      InterlockedExchangeAdd      (&(var),-(val))
#   define nx_atomic_set(var, val)      InterlockedExchange         (&(var), (val))
#   define nx_atomic_cas(var, cmp, val) ((cmp) == InterlockedCompareExchange(&(var), (val), (cmp)))

#   define nx_atomic_barrier()          MemoryBarrier()

#elif defined(NX_OS_WIN64)

#   define nx_atomic_inc(var)           InterlockedExchangeAdd64    (&(var), 1)
#   define nx_atomic_dec(var)           InterlockedExchangeAdd64    (&(var),-1)
#   define nx_atomic_add(var, val)      InterlockedExchangeAdd64    (&(var), (val))
#   define nx_atomic_sub(var, val)      InterlockedExchangeAdd64    (&(var),-(val))
#   define nx_atomic_set(var, val)      InterlockedExchange64       (&(var), (val))
#   define nx_atomic_cas(var, cmp, val) ((cmp) == InterlockedCompareExchange64(&(var), (val), (cmp)))

#   define nx_atomic_barrier()          MemoryBarrier()

#endif

#else /*NX_SINGLE_THREAD*/

namespace private_
{
    inline atomic::norm_t atomic_set_(atomic::type_t& v, atomic::norm_t n)
    {
        atomic::norm_t t = v;
        v = n;
        return t;
    }
}

#   define nx_atomic_inc(var)           (var)++
#   define nx_atomic_dec(var)           (var)--
#   define nx_atomic_add(var, val)      private_::atomic_set_((var), (var) + (val))
#   define nx_atomic_sub(var, val)      private_::atomic_set_((var), (var) - (val))
#   define nx_atomic_set(var, val)      private_::atomic_set_((var), (var))
#   define nx_atomic_cas(var, cmp, val) (((var) == (cmp)) ? (((var) = (val)), true) : false)

#   define nx_atomic_barrier()

#endif/*NX_SINGLE_THREAD*/

namespace atomic
{
    inline atomic::norm_t inc(atomic::type_t& v)
    {
        return nx_atomic_inc(v);
    }
    inline atomic::norm_t inc(atomic::type_t& v, atomic::norm_t n)
    {
        return nx_atomic_add(v, n);
    }
    inline atomic::norm_t dec(atomic::type_t& v)
    {
        return nx_atomic_dec(v);
    }
    inline atomic::norm_t dec(atomic::type_t& v, atomic::norm_t n)
    {
        return nx_atomic_sub(v, n);
    }
    inline atomic::norm_t set(atomic::type_t& v, atomic::norm_t n)
    {
        return nx_atomic_set(v, n);
    }
    inline bool cas(atomic::type_t& v, atomic::norm_t c, atomic::norm_t n)
    {
        return nx_atomic_cas(v, c, n);
    }
}

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
